{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 人脸识别案例（deprecated）\n",
    "\n",
    "本案例中，我们将学习构建一个基于深度学习技术的人脸识别模型，来对图像中的人脸进行身份识别。（本案例仅用于学习研究,，讨论交流请到[ModelArts论坛](https://bbs.huaweicloud.com/forum/forum-718-1.html)）  \n",
    "学习本案例，你将掌握MXNet AI引擎的用法（包括数据读取、模型训练和推理等功能）；掌握人脸识别模型的调参技巧、理解各个超参的含义；掌握如何基于MXNet构建一个人脸识别神经网络；掌握华为云ModelArts SDK的使用，如使用SDK创建训练作业、模型部署和模型测试等；掌握ModelArts自研的分布式训练框架MoXing的使用方法。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 步骤"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 进入华为云一站式AI开发平台ModelArts界面\n",
    "\n",
    "这步教大家如何进入华为云一站式AI开发平台华为云ModelArts服务。\n",
    "\n",
    "第一步：点击“控制台”，如下图所示\n",
    "![title](img/enter_modelarts_step1.png)\n",
    "\n",
    "第二步：点击“所有服务”，如下图所示\n",
    "![title](img/enter_modelarts_step2.png)\n",
    "\n",
    "第三步：在“EI企业智能”大类下找到“ModelArts”，点击“ModelArts”，进入ModelArts服务主界面，如下图所示\n",
    "![title](img/enter_modelarts_step3.png)\n",
    "\n",
    "第四步：看到以下界面，说明成功进入了ModelArts服务主界面\n",
    "![title](img/modelarts_index_page.png)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 创建ModelArts Notebook\n",
    "\n",
    "此步教大家如何在ModelArts中创建一个Notebook开发环境。ModelArts Notebook提供网页版的Python开发环境，无需用户自己搭建Python开发环境。\n",
    "\n",
    "第一步：点击ModelArts服务主界面中的“开发环境”，如下图所示\n",
    "![title](img/create_notebook_step1.png)\n",
    "\n",
    "第二步：点击下图中的“创建”按钮\n",
    "![title](img/create_notebook_step2.png)\n",
    "\n",
    "第三步：填写创建Notebook所需的参数：\n",
    "\n",
    "| 参数 | 说明 |\n",
    "| - - - - - | - - - - - |\n",
    "| 计费方式 | 按需计费 |\n",
    "| 名称 | 自定义名称 |\n",
    "| AI引擎 | Multi-Engine 1.0 (Python3) |\n",
    "| 资源池 | 公共资源池 |\n",
    "| 类型 | GPU |\n",
    "| 规格 | GPU: 1\\*v100NV32 CPU:8核 64GiB |\n",
    "| 存储配置 | 选择EVS，磁盘规格5GB |\n",
    "\n",
    "参数填写完毕后，点击下一步，查看Notebook实例预览信息\n",
    "\n",
    "第四步：点击下图中的“立即创建”\n",
    "![title](img/create_notebook_step4.png)\n",
    "\n",
    "第五步：点击下图中的“返回Notebook列表”\n",
    "![title](img/create_notebook_step5.png)\n",
    "\n",
    "第六步：等待Notebook创建成功，创建成功后状态会变成“运行中”，如下图所示,直接点击Notebook名字，就可以进入Notebook界面\n",
    "![title](img/create_notebook_step6.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 在ModelArts Notebook中创建一个Notebook Python开发环境\n",
    "\n",
    "第一步：点击下图所示的“打开”按钮，进入刚刚创建的Notebook\n",
    "![inter_dev_env](img/enter_dev_env.png)\n",
    "\n",
    "第二步：创建一个Notebook Python语言开发环境。先点击“New”按钮，然后根据本案例使用的AI引擎选择对应的环境\n",
    "\n",
    "第三步：重命名刚刚创建的Notebook Python开发环境。点击“Untitled”，如下图所示\n",
    "![title](img/create_notebook_dev_step2.png)\n",
    "\n",
    "第四步：填写名称。我们可以填写一个跟本实验相关的名称，然后点击“Rename”按钮，如下图所示\n",
    "![title](img/create_notebook_dev_step3.png)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 如何在Notebook Python开发环境中写代码并执行代码\n",
    "输入代码。我们打印一行字符串，按Shift+Enter(该组合键是Notebook中执行代码的快捷键)，查看代码执行结果。在代码输入框下面，可以看到代码执行结果，如下图所示\n",
    "![title](img/type_code_step1.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  准备源代码和数据\n",
    "\n",
    "这一步准备案例所需的源代码和数据，相关资源已经保存在OBS中，我们通过ModelArts SDK将资源下载到本地，并解压到当前目录下。解压后，当前目录包含src和data两个目录，分别存有源代码和数据集。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Successfully download file ai-course-common-20/face_recognition/face_recognition.tar.gz from OBS to local ./face_recognition.tar.gz\n",
      "tar: data: Cannot utime: Operation not permitted\r\n",
      "tar: data: Cannot change mode to rwsr-s---: Operation not permitted\r\n",
      "tar: src: Cannot utime: Operation not permitted\r\n",
      "tar: src: Cannot change mode to rwsr-s---: Operation not permitted\r\n",
      "tar: Exiting with failure status due to previous errors\r\n"
     ]
    }
   ],
   "source": [
    "from modelarts.session import Session\n",
    "session = Session()\n",
    "\n",
    "if session.region_name == \"cn-north-1\":\n",
    "    bucket_path = \"ai-course-common-20/face_recognition/face_recognition.tar\"\n",
    "elif session.region_name == \"cn-north-4\":\n",
    "    bucket_path = \"ai-course-common-20-bj4/face_recognition/face_recognition.tar\"\n",
    "else:\n",
    "    print(\"请更换地区到北京一或北京四\")\n",
    "    \n",
    "session.download_data(bucket_path= bucket_path, path='./face_recognition.tar.gz')\n",
    "\n",
    "# 使用tar命令解压资源包\n",
    "!tar xf ./face_recognition.tar.gz"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Notebook Python开发环境就准备好了，接下来我们使用这个开发环境进行代码实践**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 案例配置信息填写\n",
    "\n",
    "案例中需要将运行结果上传至OBS中，这一步我们设置相关的参数（使用自己真实的桶名和唯一ID替换掉*号）：\n",
    "\n",
    "- BUCKET_NAME：自己的OBS桶名\n",
    "- UNIQUE_ID：唯一ID，填写自己的IAM子账号名称或者学号"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "BUCKET_NAME = '*'\n",
    "UNIQUE_ID = '*'\n",
    "OBS_BASE_PATH = BUCKET_NAME + '/' + UNIQUE_ID"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 导入基本工具库\n",
    "\n",
    "执行下面方框中的这段代码，导入本次实验中使用的Python开发基本工具库。其中，MXNet框架是我们使用的AI编程框架，argparse库用于解析外部输入参数，src.face_resnet包含了人脸识别的卷积模型骨干。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "import os\n",
    "import mxnet as mx\n",
    "import argparse\n",
    "from src.face_resnet import get_symbol\n",
    "import warnings\n",
    "warnings.filterwarnings('ignore')\n",
    "import logging\n",
    "logger = logging.getLogger()\n",
    "logger.setLevel(logging.INFO)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 脚本入参解析\n",
    "\n",
    "该方法用来解析用户设置的训练所需的超参和数据路径等参数，如初始学习率、权重衰减系数、batch size等，使得这些参数可以从脚本外面控制，而不需要从代码中修改。\n",
    "\n",
    "部分参数说明如下：\n",
    "\n",
    "image_size：图像的大小；\n",
    "\n",
    "lr： 学习率learning rate，表示梯度往某个方向更新的步长系数；\n",
    "\n",
    "wd： 权值衰减系数weight decay，表示在损失函数计算时，L2归一化项的衰减系数；\n",
    "\n",
    "epoch_image_num： 在一个epoch中，训练的数据量大小，一般为整个数据集的数据量大小；\n",
    "\n",
    "num_classes： 分类数，表示本次数据集的类别数量；\n",
    "\n",
    "num_epoch: epoch是模型训练整个数据集的一次迭代，num_epoch也就代表了整个训练过程对数据集的迭代次数，它的值越大那么模型学习就更加深，在训练集上拟合效果更加好，但过大的迭代次数也可能导致过拟合现象，也即是在训练集上表现好，在验证集或测试集等其他数据上表现很差；\n",
    "\n",
    "学习率lr：模型一次训练得到的梯度代表了模型参数更新的方向和大小，而学习率是这个方向上更新大小的权值系数，太大的学习率会导致这次“迈出的一步”过大，太小则会导致“这一步”过小，也即是学习率对这一次参数更新大小起到了决定作用。于是很好理解，在一个epoch里若是参数更新次数不变化，更大的学习率，会使参数更新“走过的路程”更远，学习也更加深。\n",
    "    \n",
    "当然，学习率并不是越大越好，因为参数更新的方向是变化的，过大的学习率会导致学习不稳定。通常学习率大小都在0.1-0.0001之间调整。\n",
    "\n",
    "per_batch_size: per_batch_size是指在每个GPU上一次训练一批数据的大小，调整这个值越大，表示一次训练的数据量也就越大，一次训练的数据量和训练有如下关系：\n",
    "    \n",
    "![title](img/batch_size.png)\n",
    "\n",
    "这个公式代表了梯度更新的方法大小，m为batch_size大小，Var是指方差，显然当batch_size越大，方差也就越大，而众所周知方差越大，也就表示数据越稳定，在深度学习中也就表示训练得越稳定。\n",
    "\n",
    "于是结合上面讲的学习率大小的调整，在显存可承受范围内，越大的batch_size使得学习更稳定情况下，学习率也可以随着调整得更大。\n",
    "        \n",
    "\n",
    "动量mom: 是动量的随机梯度下降中的一个参数，动量越小会使得模型参数的更新越稳定，过小的动量也会使得模型几乎不会学习，相反过大也会导致梯度波动也越大。动量大小大小一般在0.5-0.999之间调整，而常用的大小为0.9，太大太小都会导致模型训练不稳定。\n",
    "\n",
    "num_layers: 表示使用的ResNet的卷积层层数，越大的层数模型越就越复杂，得到的精度可能更好，但是训练速度也相应更小了。\n",
    "    \n",
    "num_embed： 表示使用的人脸识别模型的嵌入层神经元数量，数值越大也就表示嵌入层表示的特征信息越丰富，而因此训练速度也会受到影响。\n",
    "\n",
    "此段代码只设置脚本参数，无代码执行输出。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def parse_args():\n",
    "    parser = argparse.ArgumentParser(description='Train face network')\n",
    "    parser.add_argument('--data_dir', default='data',\n",
    "                        help='training set directory')# 训练数据路径\n",
    "    parser.add_argument('--train_url', type=str, default='ckpt',\n",
    "                        help='train path')# 训练模型输出路径\n",
    "    parser.add_argument('--num_epoch', type=int, default=20,\n",
    "                        help='training epoch size.')# 训练epoch数量\n",
    "    parser.add_argument('--num_layers', type=int, default=18,\n",
    "                        help='specify network layer, 18 50 100')# 使用的ResNet卷积层层数\n",
    "    parser.add_argument('--image_size', default='112,112',\n",
    "                        help='specify input image height and width')# 输入图像的大小，高、宽\n",
    "    parser.add_argument('--lr', type=float, default=0.005,\n",
    "                        help='start learning rate')# 训练开始的学习率大小\n",
    "    parser.add_argument('--wd', type=float, default=0.0005,\n",
    "                        help='weight decay')# 权值衰减系数\n",
    "    parser.add_argument('--mom', type=float, default=0.9,\n",
    "                        help='momentum')# 动量\n",
    "    parser.add_argument('--per_batch_size', type=int, default=8,\n",
    "                        help='batch size in each context')# 每一个GPU的batch size大小\n",
    "    parser.add_argument('--epoch_image_num', type=int, default=200,\n",
    "                        help='image size in one epoch')# 训练样本大小\n",
    "    parser.add_argument('--train_file', type=str, default='celeb_train.rec',\n",
    "                        help='train')# 训练数据文件\n",
    "    parser.add_argument('--val_file', type=str, default='celeb_val.rec',\n",
    "                        help='val')# 验证集文件\n",
    "    parser.add_argument('--num_classes', type=int, default=5,\n",
    "                        help='classes number')# 分类数\n",
    "    parser.add_argument('--num_embed', type=int, default=8,\n",
    "                        help='embedding number')# 嵌入层神经元数量\n",
    "    parser.add_argument('--num_gpus', type=int, default=1,\n",
    "                        help='GPUs number')# GPU数量\n",
    "    parser.add_argument('--save_frequency', type=int, default=1,\n",
    "                        help='save frequency')# 保存模型的频率\n",
    "    parser.add_argument('--export_model', type=bool, default=False,\n",
    "                        help='change train url to model,metric.json')#是否改变train url结构\n",
    "    \n",
    "    args, _ = parser.parse_known_args()\n",
    "    return args\n",
    "\n",
    "args = parse_args()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 设置超参\n",
    "设置训练需要的超参：\n",
    "\n",
    "1. 设置模型训练的设备环境;\n",
    "2. 设置训练时的batch size，固定图像大小的信息，和梯度归一化等参数\n",
    "3. 设置优化器参数\n",
    "4. 设置训练时的回调函数\n",
    "5. 设置模型参数初始化方法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 设置模型训练使用的设备环境\n",
    "这里设置模型所在的设备环境，会输出相应的设备号，如\n",
    "\n",
    "    [cpu(0)]\n",
    "\n",
    "或者\n",
    "\n",
    "    [gpu(0)]\n",
    "    \n",
    "本案例中，我们使用GPU训练环境，这段代码会输出[gpu(0)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[gpu(0)]\n"
     ]
    }
   ],
   "source": [
    "# 模型所在的环境，指cpu或者哪块gpu\n",
    "ctx = [mx.gpu(x) for x in range(args.num_gpus)] if args.num_gpus>0 else [mx.cpu()]\n",
    "print(ctx)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 设置训练时的batch size，固定图像大小的信息，和梯度归一化的参数\n",
    "\n",
    "rescale grad含义：在多GPU设备的环境下，需要将梯度推送到参数服务器中做“和”操作，rescale grad表示在推送到参数服务器前先将梯度做一个相应的归一化，这里的归一化系数rescale grad的值为：1.0 / GPU的数量 / 每个GPU上batch_size的大小。\n",
    "\n",
    "这里会输出batch size、image height、image width和rescale grad的信息"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "batch size:  8\n",
      "image height:  112 , image width:  112\n",
      "rescale grad:  0.125\n"
     ]
    }
   ],
   "source": [
    "# 一次训练输入的batch size大小的数据\n",
    "batch_size = int(args.per_batch_size * (args.num_gpus if args.num_gpus>0 else 1))\n",
    "print('batch size: ', batch_size)\n",
    "# 输入图像的高和宽\n",
    "image_h, image_w = [int(x) for x in args.image_size.split(',')]\n",
    "print('image height: ', image_h, ', image width: ', image_w)\n",
    "# 梯度归一化参数，大小为1.0 / batch_size\n",
    "_rescale = 1.0 / batch_size\n",
    "print('rescale grad: ', _rescale)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 设置优化器参数\n",
    "设置本次训练的优化器参数，这里仅将学习率固定为某一个值，可以尝试减小或增大学习率来得到更好的训练结果。\n",
    "\n",
    "本段代码执行后会输出优化器的所有参数信息，包含学习率、权值衰减系数等。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'learning_rate': 0.005, 'wd': 0.0005, 'lr_scheduler': None, 'clip_gradient': 5, 'momentum': 0.9, 'multi_precision': True, 'rescale_grad': 0.125}\n"
     ]
    }
   ],
   "source": [
    "# 优化器参数，包含学习率调度器、权值衰减系数等\n",
    "optimizer_params = {'learning_rate': args.lr,\n",
    "                    'wd' : args.wd,\n",
    "                    'lr_scheduler': None,\n",
    "                    'clip_gradient': 5,\n",
    "                    'momentum' : args.mom,\n",
    "                    'multi_precision' : True,\n",
    "                    'rescale_grad': _rescale,\n",
    "                    }\n",
    "print(optimizer_params)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 设置训练时的回调函数\n",
    "\n",
    "训练时的回调函数中定义每一个step训练完后的操作和每一个epoch训练完后的操作，其中一个step指完成一个batch size大小数据的训练，一个epoch是指完成一遍整个数据集的训练。\n",
    "\n",
    "每一个step训练完后：计算训练的吞吐量，在设置了模型评估方法后，也会计算相应的评估函数，如这里的准确度和交叉熵。\n",
    "\n",
    "每一个epoch训练完后：保存已经训练好的模型文件。\n",
    "\n",
    "此段代码只设置回调函数，无代码执行输出。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 每一个step训练完后的操作\n",
    "batch_end_callbacks = [mx.callback.Speedometer(batch_size, 10, auto_reset=True)]\n",
    "# 每一个epoch训练完后的操作\n",
    "# 这里的模型保存路径为模型输出文件目录下的model目录，模型文件以face开头\n",
    "epoch_end_callbacks = mx.callback.do_checkpoint(\n",
    "    os.path.join(args.train_url,'model/face'), args.save_frequency)\n",
    "# 模型评估方法，准确度和交叉熵\n",
    "eval_metrics = [mx.metric.Accuracy(), mx.metric.CrossEntropy()]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 设置模型初始化参数的方法\n",
    "\n",
    "设置神经网络参数初始化方法，这里使用的是高斯分布的Xavier初始化方法。还可以选择其他方法如mx.init.MSRAPrelu()等，不同的初始化参数会有不同的模型训练结果。\n",
    "\n",
    "此段代码只设置初始化参数方法，无代码执行输出。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 模型参数的初始化方法\n",
    "initializer = mx.init.Xavier(rnd_type='gaussian', factor_type=\"out\", magnitude=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 读取人脸数据集\n",
    "\n",
    "#### 使用MXNet读取数据集\n",
    "\n",
    "人脸识别数据集使用的是明星人脸图像集CelebFace，经过人脸3D对齐等操作制作成了适合分类模型训练的数据，每张图像的大小为112*112，样本图像如下：\n",
    "    \n",
    "![title](img/test.png) ![title](img/test2.png)\n",
    "\n",
    "数据集格式为MXNet专用的rec格式，rec格式的数据集相比原图读取速度更快。\n",
    "\n",
    "这里使用MXNet自带的mx.io.ImageRecordIter方法读取rec格式数据集，mx.io.ImageRecordIter可以在读取数据时就实现数据增强的方法，这里做了随机翻转、数据集重新洗牌等增强。\n",
    "    \n",
    "这里定义数据加载器的对象，加载成功会打印输出 “data loaded!”。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "data loaded!\n"
     ]
    }
   ],
   "source": [
    "# 训练集和验证集的rec文件路径\n",
    "path_imgrec = os.path.join(args.data_dir, args.train_file)\n",
    "path_val_imgrec = os.path.join(args.data_dir, args.val_file)\n",
    "# 使用mxnet自带的mx.io.ImageRecordIter加载数据\n",
    "val_dataiter = mx.io.ImageRecordIter(\n",
    "        path_imgrec=path_val_imgrec,\n",
    "        data_shape=(3, image_h, image_w),\n",
    "        batch_size=batch_size,\n",
    "        resize=image_w,                 # 图像按照其最短边resize到定义好的宽长\n",
    "        shuffle=False,\n",
    "        preprocess_threads=2)\n",
    "train_dataiter = mx.io.ImageRecordIter(\n",
    "        path_imgrec=path_imgrec,\n",
    "        data_shape=(3, image_h, image_w),\n",
    "        num_parts=1,                    # 训练的节点数\n",
    "        part_index=0,                   # 本机所在的节点id\n",
    "        batch_size=batch_size,\n",
    "        resize=image_w,\n",
    "        rand_crop=False,\n",
    "        rand_mirror=True,              # 图像随机翻转\n",
    "        shuffle=True,                  # 数据集随机洗牌\n",
    "        preprocess_threads=2)\n",
    "print('data loaded!')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 查看数据集图像\n",
    "matplotlib.pyplot可以绘制基本图形，读取上段代码数据加载器中的某张图片，并使用matplotlib.pyplot输出上面读取到的数据集中样本人脸图像。\n",
    "\n",
    "此段代码会执行输出一张人脸图片。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fd3a40fa198>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "next_data_batch = next(iter(train_dataiter))\n",
    "img = next_data_batch.data[0][0].transpose(axes=(2,1,0))\n",
    "img = img.asnumpy().astype('uint8')\n",
    "# 画图，数据增强后的图片\n",
    "%matplotlib inline\n",
    "fig = plt.figure()\n",
    "plt.imshow(img)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 人脸识别神经网络构建\n",
    "\n",
    "本教程介绍的人脸识别算法使用的模型骨干（backbone）为ResNet-50，我们基于业界流行的ResNet-50模型构建我们的人脸识别神经网络。\n",
    "\n",
    "从本教程的src目录下的face_resnet.py源代码文件加载模型骨干，并将其分类层作为嵌入层，分类数设置为嵌入层神经元数。\n",
    "\n",
    "在嵌入层后面接一个分类数为num_classes的分类层，就可以得到本实验的人脸识别神经网络了。\n",
    "\n",
    "这里会输出一个 mx.mod.Module 对象。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<mxnet.module.module.Module object at 0x7fd4340200b8>\n"
     ]
    }
   ],
   "source": [
    "# 初始化模型参数为None，这里可以加载预训练的模型参数\n",
    "arg_params = None\n",
    "aux_params = None\n",
    "# 得到嵌入层模型的Symbol\n",
    "embedding = get_symbol(num_classes=args.num_embed, num_layers=args.num_layers)\n",
    "# 设置分类层的权值\n",
    "_weight = mx.symbol.Variable(\"fc7_weight\",\n",
    "                             shape=(args.num_classes, args.num_embed),\n",
    "                             lr_mult=1.0, wd_mult=1.0)\n",
    "_bias = mx.symbol.Variable('fc7_bias', lr_mult=2.0, wd_mult=0.0)\n",
    "# 全连接层\n",
    "fc7 = mx.sym.FullyConnected(data=embedding, weight = _weight,\n",
    "                            bias = _bias, num_hidden=args.num_classes, name='fc7')\n",
    "all_label = mx.symbol.Variable('softmax_label')\n",
    "softmax = mx.symbol.SoftmaxOutput(data=fc7, label = all_label, name='softmax')\n",
    "# 输入模型环境和Symbol\n",
    "model = mx.mod.Module(context=ctx, symbol=softmax)\n",
    "print(model)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 训练\n",
    "使用mx.mod.Module类自带的fit函数进行训练，约在10个epoch左右，在验证集上可以达到0.75的准确率。\n",
    "\n",
    "本段代码执行成功后，会在下方输出每一步训练的日志，如\n",
    "\n",
    "    INFO:root:Epoch[0] Batch [10]\tSpeed: 1.38 samples/sec\taccuracy=0.397727\tcross-entropy=1.965782\n",
    "    INFO:root:Epoch[0] Batch [20]\tSpeed: 1.49 samples/sec\taccuracy=0.512500\tcross-entropy=1.576477\n",
    "    INFO:root:Epoch[0] Train-accuracy=0.583333\n",
    "    INFO:root:Epoch[0] Train-cross-entropy=1.028852\n",
    "    INFO:root:Epoch[0] Time cost=130.980\n",
    "    INFO:root:Saved checkpoint to \"s3://ai-course-001/face_recognition/ckpt/model/face-0001.params\"\n",
    "    INFO:root:Epoch[0] Validation-accuracy=0.583333\n",
    "    INFO:root:Epoch[0] Validation-cross-entropy=1.360136\n",
    "\n",
    "本段日志描述了模型训练的日志，每一步训练都输出了模型的准确率、模型交叉熵损失等，可以看到在每一步训练模型的准确率都越来越高，模型交叉熵损失越来越低，是模型训练得越来越好的标志。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:root:Epoch[0] Batch [10]\tSpeed: 172.94 samples/sec\taccuracy=0.443182\tcross-entropy=2.185036\n",
      "INFO:root:Epoch[0] Batch [20]\tSpeed: 179.97 samples/sec\taccuracy=0.525000\tcross-entropy=1.391039\n",
      "INFO:root:Epoch[0] Train-accuracy=0.791667\n",
      "INFO:root:Epoch[0] Train-cross-entropy=0.813433\n",
      "INFO:root:Epoch[0] Time cost=1.912\n",
      "INFO:root:Saved checkpoint to \"ckpt/model/face-0001.params\"\n",
      "INFO:root:Epoch[0] Validation-accuracy=0.541667\n",
      "INFO:root:Epoch[0] Validation-cross-entropy=2.005202\n",
      "INFO:root:Epoch[1] Batch [10]\tSpeed: 207.27 samples/sec\taccuracy=0.715909\tcross-entropy=0.842773\n",
      "INFO:root:Epoch[1] Batch [20]\tSpeed: 176.77 samples/sec\taccuracy=0.750000\tcross-entropy=0.836055\n",
      "INFO:root:Epoch[1] Train-accuracy=0.666667\n",
      "INFO:root:Epoch[1] Train-cross-entropy=0.889418\n",
      "INFO:root:Epoch[1] Time cost=1.016\n",
      "INFO:root:Saved checkpoint to \"ckpt/model/face-0002.params\"\n",
      "INFO:root:Epoch[1] Validation-accuracy=0.562500\n",
      "INFO:root:Epoch[1] Validation-cross-entropy=1.230389\n",
      "INFO:root:Epoch[2] Batch [10]\tSpeed: 211.34 samples/sec\taccuracy=0.806818\tcross-entropy=0.505878\n",
      "INFO:root:Epoch[2] Batch [20]\tSpeed: 160.25 samples/sec\taccuracy=0.800000\tcross-entropy=0.628947\n",
      "INFO:root:Epoch[2] Train-accuracy=0.958333\n",
      "INFO:root:Epoch[2] Train-cross-entropy=0.303491\n",
      "INFO:root:Epoch[2] Time cost=1.072\n",
      "INFO:root:Saved checkpoint to \"ckpt/model/face-0003.params\"\n",
      "INFO:root:Epoch[2] Validation-accuracy=0.708333\n",
      "INFO:root:Epoch[2] Validation-cross-entropy=1.015811\n",
      "INFO:root:Epoch[3] Batch [10]\tSpeed: 173.58 samples/sec\taccuracy=0.852273\tcross-entropy=0.455078\n",
      "INFO:root:Epoch[3] Batch [20]\tSpeed: 220.57 samples/sec\taccuracy=0.900000\tcross-entropy=0.382174\n",
      "INFO:root:Epoch[3] Train-accuracy=1.000000\n",
      "INFO:root:Epoch[3] Train-cross-entropy=0.300285\n",
      "INFO:root:Epoch[3] Time cost=1.034\n",
      "INFO:root:Saved checkpoint to \"ckpt/model/face-0004.params\"\n",
      "INFO:root:Epoch[3] Validation-accuracy=0.625000\n",
      "INFO:root:Epoch[3] Validation-cross-entropy=0.898546\n",
      "INFO:root:Epoch[4] Batch [10]\tSpeed: 195.14 samples/sec\taccuracy=0.886364\tcross-entropy=0.426602\n",
      "INFO:root:Epoch[4] Batch [20]\tSpeed: 189.95 samples/sec\taccuracy=0.762500\tcross-entropy=0.596789\n",
      "INFO:root:Epoch[4] Train-accuracy=0.812500\n",
      "INFO:root:Epoch[4] Train-cross-entropy=0.456027\n",
      "INFO:root:Epoch[4] Time cost=1.065\n",
      "INFO:root:Saved checkpoint to \"ckpt/model/face-0005.params\"\n",
      "INFO:root:Epoch[4] Validation-accuracy=0.687500\n",
      "INFO:root:Epoch[4] Validation-cross-entropy=0.927765\n",
      "INFO:root:Epoch[5] Batch [10]\tSpeed: 176.01 samples/sec\taccuracy=0.852273\tcross-entropy=0.455081\n",
      "INFO:root:Epoch[5] Batch [20]\tSpeed: 186.62 samples/sec\taccuracy=0.837500\tcross-entropy=0.435856\n",
      "INFO:root:Epoch[5] Train-accuracy=1.000000\n",
      "INFO:root:Epoch[5] Train-cross-entropy=0.102141\n",
      "INFO:root:Epoch[5] Time cost=1.094\n",
      "INFO:root:Saved checkpoint to \"ckpt/model/face-0006.params\"\n",
      "INFO:root:Epoch[5] Validation-accuracy=0.708333\n",
      "INFO:root:Epoch[5] Validation-cross-entropy=0.850218\n",
      "INFO:root:Epoch[6] Batch [10]\tSpeed: 198.19 samples/sec\taccuracy=0.897727\tcross-entropy=0.252100\n",
      "INFO:root:Epoch[6] Batch [20]\tSpeed: 188.84 samples/sec\taccuracy=0.875000\tcross-entropy=0.441260\n",
      "INFO:root:Epoch[6] Train-accuracy=0.916667\n",
      "INFO:root:Epoch[6] Train-cross-entropy=0.311970\n",
      "INFO:root:Epoch[6] Time cost=0.979\n",
      "INFO:root:Saved checkpoint to \"ckpt/model/face-0007.params\"\n",
      "INFO:root:Epoch[6] Validation-accuracy=0.625000\n",
      "INFO:root:Epoch[6] Validation-cross-entropy=1.177226\n",
      "INFO:root:Epoch[7] Batch [10]\tSpeed: 182.19 samples/sec\taccuracy=0.931818\tcross-entropy=0.309536\n",
      "INFO:root:Epoch[7] Batch [20]\tSpeed: 220.97 samples/sec\taccuracy=0.950000\tcross-entropy=0.215843\n",
      "INFO:root:Epoch[7] Train-accuracy=0.916667\n",
      "INFO:root:Epoch[7] Train-cross-entropy=0.215555\n",
      "INFO:root:Epoch[7] Time cost=1.002\n",
      "INFO:root:Saved checkpoint to \"ckpt/model/face-0008.params\"\n",
      "INFO:root:Epoch[7] Validation-accuracy=0.666667\n",
      "INFO:root:Epoch[7] Validation-cross-entropy=0.995754\n",
      "INFO:root:Epoch[8] Batch [10]\tSpeed: 186.96 samples/sec\taccuracy=0.965909\tcross-entropy=0.154120\n",
      "INFO:root:Epoch[8] Batch [20]\tSpeed: 191.38 samples/sec\taccuracy=0.975000\tcross-entropy=0.176552\n",
      "INFO:root:Epoch[8] Train-accuracy=0.968750\n",
      "INFO:root:Epoch[8] Train-cross-entropy=0.121073\n",
      "INFO:root:Epoch[8] Time cost=1.082\n",
      "INFO:root:Saved checkpoint to \"ckpt/model/face-0009.params\"\n",
      "INFO:root:Epoch[8] Validation-accuracy=0.625000\n",
      "INFO:root:Epoch[8] Validation-cross-entropy=0.959331\n",
      "INFO:root:Epoch[9] Batch [10]\tSpeed: 182.08 samples/sec\taccuracy=0.954545\tcross-entropy=0.223226\n",
      "INFO:root:Epoch[9] Batch [20]\tSpeed: 212.42 samples/sec\taccuracy=0.912500\tcross-entropy=0.281093\n",
      "INFO:root:Epoch[9] Train-accuracy=0.958333\n",
      "INFO:root:Epoch[9] Train-cross-entropy=0.202880\n",
      "INFO:root:Epoch[9] Time cost=1.021\n",
      "INFO:root:Saved checkpoint to \"ckpt/model/face-0010.params\"\n",
      "INFO:root:Epoch[9] Validation-accuracy=0.562500\n",
      "INFO:root:Epoch[9] Validation-cross-entropy=1.334065\n",
      "INFO:root:Epoch[10] Batch [10]\tSpeed: 188.21 samples/sec\taccuracy=0.965909\tcross-entropy=0.177402\n",
      "INFO:root:Epoch[10] Batch [20]\tSpeed: 184.77 samples/sec\taccuracy=0.900000\tcross-entropy=0.250768\n",
      "INFO:root:Epoch[10] Train-accuracy=1.000000\n",
      "INFO:root:Epoch[10] Train-cross-entropy=0.191520\n",
      "INFO:root:Epoch[10] Time cost=1.037\n",
      "INFO:root:Saved checkpoint to \"ckpt/model/face-0011.params\"\n",
      "INFO:root:Epoch[10] Validation-accuracy=0.750000\n",
      "INFO:root:Epoch[10] Validation-cross-entropy=0.695873\n",
      "INFO:root:Epoch[11] Batch [10]\tSpeed: 186.78 samples/sec\taccuracy=0.920455\tcross-entropy=0.165103\n",
      "INFO:root:Epoch[11] Batch [20]\tSpeed: 200.42 samples/sec\taccuracy=0.950000\tcross-entropy=0.135902\n",
      "INFO:root:Epoch[11] Train-accuracy=1.000000\n",
      "INFO:root:Epoch[11] Train-cross-entropy=0.132742\n",
      "INFO:root:Epoch[11] Time cost=1.019\n",
      "INFO:root:Saved checkpoint to \"ckpt/model/face-0012.params\"\n",
      "INFO:root:Epoch[11] Validation-accuracy=0.625000\n",
      "INFO:root:Epoch[11] Validation-cross-entropy=0.925625\n",
      "INFO:root:Epoch[12] Batch [10]\tSpeed: 182.99 samples/sec\taccuracy=0.977273\tcross-entropy=0.143543\n",
      "INFO:root:Epoch[12] Batch [20]\tSpeed: 205.37 samples/sec\taccuracy=0.987500\tcross-entropy=0.121799\n",
      "INFO:root:Epoch[12] Train-accuracy=1.000000\n",
      "INFO:root:Epoch[12] Train-cross-entropy=0.094597\n",
      "INFO:root:Epoch[12] Time cost=1.081\n",
      "INFO:root:Saved checkpoint to \"ckpt/model/face-0013.params\"\n",
      "INFO:root:Epoch[12] Validation-accuracy=0.687500\n",
      "INFO:root:Epoch[12] Validation-cross-entropy=0.962060\n",
      "INFO:root:Epoch[13] Batch [10]\tSpeed: 170.36 samples/sec\taccuracy=0.988636\tcross-entropy=0.107761\n",
      "INFO:root:Epoch[13] Batch [20]\tSpeed: 178.38 samples/sec\taccuracy=0.950000\tcross-entropy=0.251849\n",
      "INFO:root:Epoch[13] Train-accuracy=1.000000\n",
      "INFO:root:Epoch[13] Train-cross-entropy=0.108509\n",
      "INFO:root:Epoch[13] Time cost=1.086\n",
      "INFO:root:Saved checkpoint to \"ckpt/model/face-0014.params\"\n",
      "INFO:root:Epoch[13] Validation-accuracy=0.708333\n",
      "INFO:root:Epoch[13] Validation-cross-entropy=1.034108\n",
      "INFO:root:Epoch[14] Batch [10]\tSpeed: 175.84 samples/sec\taccuracy=0.965909\tcross-entropy=0.218981\n",
      "INFO:root:Epoch[14] Batch [20]\tSpeed: 217.40 samples/sec\taccuracy=0.950000\tcross-entropy=0.157839\n",
      "INFO:root:Epoch[14] Train-accuracy=1.000000\n",
      "INFO:root:Epoch[14] Train-cross-entropy=0.108156\n",
      "INFO:root:Epoch[14] Time cost=1.012\n",
      "INFO:root:Saved checkpoint to \"ckpt/model/face-0015.params\"\n",
      "INFO:root:Epoch[14] Validation-accuracy=0.625000\n",
      "INFO:root:Epoch[14] Validation-cross-entropy=0.982524\n",
      "INFO:root:Epoch[15] Batch [10]\tSpeed: 179.07 samples/sec\taccuracy=0.954545\tcross-entropy=0.161932\n",
      "INFO:root:Epoch[15] Batch [20]\tSpeed: 186.64 samples/sec\taccuracy=0.975000\tcross-entropy=0.122577\n",
      "INFO:root:Epoch[15] Train-accuracy=1.000000\n",
      "INFO:root:Epoch[15] Train-cross-entropy=0.093556\n",
      "INFO:root:Epoch[15] Time cost=1.021\n",
      "INFO:root:Saved checkpoint to \"ckpt/model/face-0016.params\"\n",
      "INFO:root:Epoch[15] Validation-accuracy=0.708333\n",
      "INFO:root:Epoch[15] Validation-cross-entropy=0.724650\n",
      "INFO:root:Epoch[16] Batch [10]\tSpeed: 183.26 samples/sec\taccuracy=0.943182\tcross-entropy=0.150030\n",
      "INFO:root:Epoch[16] Batch [20]\tSpeed: 188.40 samples/sec\taccuracy=0.975000\tcross-entropy=0.115663\n",
      "INFO:root:Epoch[16] Train-accuracy=0.906250\n",
      "INFO:root:Epoch[16] Train-cross-entropy=0.267720\n",
      "INFO:root:Epoch[16] Time cost=1.105\n",
      "INFO:root:Saved checkpoint to \"ckpt/model/face-0017.params\"\n",
      "INFO:root:Epoch[16] Validation-accuracy=0.625000\n",
      "INFO:root:Epoch[16] Validation-cross-entropy=0.764140\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:root:Epoch[17] Batch [10]\tSpeed: 197.45 samples/sec\taccuracy=0.988636\tcross-entropy=0.108683\n",
      "INFO:root:Epoch[17] Batch [20]\tSpeed: 181.92 samples/sec\taccuracy=1.000000\tcross-entropy=0.075606\n",
      "INFO:root:Epoch[17] Train-accuracy=1.000000\n",
      "INFO:root:Epoch[17] Train-cross-entropy=0.059592\n",
      "INFO:root:Epoch[17] Time cost=1.054\n",
      "INFO:root:Saved checkpoint to \"ckpt/model/face-0018.params\"\n",
      "INFO:root:Epoch[17] Validation-accuracy=0.687500\n",
      "INFO:root:Epoch[17] Validation-cross-entropy=0.876016\n",
      "INFO:root:Epoch[18] Batch [10]\tSpeed: 184.30 samples/sec\taccuracy=0.988636\tcross-entropy=0.062771\n",
      "INFO:root:Epoch[18] Batch [20]\tSpeed: 194.51 samples/sec\taccuracy=1.000000\tcross-entropy=0.072895\n",
      "INFO:root:Epoch[18] Train-accuracy=1.000000\n",
      "INFO:root:Epoch[18] Train-cross-entropy=0.061150\n",
      "INFO:root:Epoch[18] Time cost=1.024\n",
      "INFO:root:Saved checkpoint to \"ckpt/model/face-0019.params\"\n",
      "INFO:root:Epoch[18] Validation-accuracy=0.750000\n",
      "INFO:root:Epoch[18] Validation-cross-entropy=0.768125\n",
      "INFO:root:Epoch[19] Batch [10]\tSpeed: 174.84 samples/sec\taccuracy=0.977273\tcross-entropy=0.089337\n",
      "INFO:root:Epoch[19] Batch [20]\tSpeed: 211.37 samples/sec\taccuracy=0.987500\tcross-entropy=0.083705\n",
      "INFO:root:Epoch[19] Train-accuracy=1.000000\n",
      "INFO:root:Epoch[19] Train-cross-entropy=0.045997\n",
      "INFO:root:Epoch[19] Time cost=1.018\n",
      "INFO:root:Saved checkpoint to \"ckpt/model/face-0020.params\"\n",
      "INFO:root:Epoch[19] Validation-accuracy=0.708333\n",
      "INFO:root:Epoch[19] Validation-cross-entropy=0.872740\n"
     ]
    }
   ],
   "source": [
    "model_output_dir = args.train_url + '/model/'\n",
    "\n",
    "if os.path.exists(model_output_dir) == False:\n",
    "    os.makedirs(model_output_dir)\n",
    "\n",
    "model.fit(train_dataiter,\n",
    "          begin_epoch        = 0,\n",
    "          num_epoch          = args.num_epoch,\n",
    "          eval_data          = val_dataiter,\n",
    "          eval_metric        = eval_metrics,\n",
    "          kvstore            = 'local',\n",
    "          optimizer          = 'nag',\n",
    "          optimizer_params   = optimizer_params,\n",
    "          initializer        = initializer,\n",
    "          arg_params         = arg_params,\n",
    "          aux_params         = aux_params,\n",
    "          allow_missing      = True,\n",
    "          batch_end_callback = batch_end_callbacks,\n",
    "          epoch_end_callback = epoch_end_callbacks )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来，我们将训练得到的模型上传到自己的OBS目录下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:root:Using MoXing-v1.12.4-85d16fc8\n",
      "INFO:root:Using OBS-Python-SDK-3.1.2\n",
      "INFO:root:Start to copy 21 files.\n",
      "INFO:root:Finish copy.\n"
     ]
    }
   ],
   "source": [
    "import moxing as mox\n",
    "\n",
    "# The obs_dst_path pattern： {BUCKET_NAME}/{UNIQUE_ID}/face_recognition/results/{TRAIN_URL}\n",
    "obs_dst_path = \"s3://\" + OBS_BASE_PATH + \"/face_recognition/results/\" + args.train_url + '/'\n",
    "mox.file.copy_parallel(args.train_url+'/model',\n",
    "                      obs_dst_path)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<p style=\"font-family: Arial; font-size:1.4em;color:gold;\">总结：训练完成后，在训练模型输出的OBS路径下（参考上述代码中的obs_dst_path），有model文件夹，model文件夹下面会有.params和.json后缀的模型文件。</p>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 推理\n",
    "\n",
    "模型推理共分为以下几个步骤：\n",
    "1. 导入读取图片所需的库；\n",
    "2. 输出测试的原图；\n",
    "3. 将测试原图转换成模型需要的数据；\n",
    "4. 加载推理使用的模型结构；\n",
    "5. 推理计算并输出结果；\n",
    "\n",
    "#### 导入读取图片所需的库\n",
    "MoXing为华为自研的分布式深度学习框架，在这里用于notebook访问OBS。\n",
    "\n",
    "mxnet.image里包含了一些常用的图像数据处理方法。\n",
    "\n",
    "此段代码无回显。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "import mxnet.image as image\n",
    "import moxing as mox\n",
    "import mxnet as mx\n",
    "import matplotlib.pyplot as plt\n",
    "import os"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 输出测试的原图\n",
    "这里使用matplotlib.pyplot来输出我们需要测试的图片，由于不能知道每张人脸的真实名字，用数字来替代\n",
    "\n",
    "此段代码会输出一张人脸图像"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fd433eee710>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 设置类别名，由于不能知道每张人脸的真实名字，用数字来替代\n",
    "class_names = range(5)\n",
    "# 人脸图像路径,这里可以任意选择一张人脸图像，并上传到自己的OBS路径\n",
    "# 如s3://ai-course-001/face_recognition/data/下的test.png\n",
    "file_name = args.data_dir + '/test.png'\n",
    "# 以二进制形式，将图片读入内存\n",
    "img = mox.file.read(file_name, binary=True)\n",
    "# 查看图片\n",
    "orig_img = mx.img.imdecode(img, to_rgb=1).asnumpy().astype('uint8')\n",
    "plt.imshow(orig_img)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 将测试原图转换成模型推理需要的格式\n",
    "这里使用mxnet.image来对测试原图进行增强，这里仅进行短边resize到定义好的固定的图像边大小112，之后再将图像翻转和维度扩展，最后得到模型需要的数据。\n",
    "\n",
    "这里会输出 (NDArray 1x3x112x112 @cpu(0)) 格式数据；"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[\n",
      "[[[[  4   0   0 ...,  84  74  62]\n",
      "   [  4   0   0 ...,  59  52  45]\n",
      "   [  3   0   1 ...,  35  33  32]\n",
      "   ..., \n",
      "   [ 20  15  16 ..., 122 114 102]\n",
      "   [ 22  13  10 ..., 120 110  99]\n",
      "   [ 25  15   9 ..., 118 105  95]]\n",
      "\n",
      "  [[  6   2   1 ...,  75  65  53]\n",
      "   [  6   2   1 ...,  50  43  36]\n",
      "   [  5   2   2 ...,  26  24  23]\n",
      "   ..., \n",
      "   [ 24  19  18 ..., 104  96  83]\n",
      "   [ 24  15  10 ..., 102  92  80]\n",
      "   [ 27  17   9 ..., 100  87  76]]\n",
      "\n",
      "  [[  3   0   0 ...,  58  48  36]\n",
      "   [  3   0   0 ...,  33  26  19]\n",
      "   [  2   0   0 ...,   9   9   8]\n",
      "   ..., \n",
      "   [ 23  18  15 ...,  80  76  66]\n",
      "   [ 23  14   8 ...,  78  72  63]\n",
      "   [ 26  16   7 ...,  76  67  59]]]]\n",
      "<NDArray 1x3x112x112 @cpu(0)>]\n"
     ]
    }
   ],
   "source": [
    "# 图像处理\n",
    "# 将图像处理成(3,112,112)大小\n",
    "[h, w] = [112, 112]\n",
    "img_arr = mx.img.imdecode(img, to_rgb=1)\n",
    "# 首先转换成NDArray形式\n",
    "img_arr = mx.img.resize_short(img_arr, w, 0)\n",
    "# 翻转，经过翻转的图像不能通过plt显示\n",
    "img_arr = mx.nd.transpose(img_arr, (2, 0, 1))\n",
    "img_arr = mx.nd.expand_dims(img_arr, axis=0)\n",
    "# 将图像绑定到cpu上\n",
    "d = [img_arr.as_in_context(mx.cpu())]\n",
    "print(d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 加载推理使用的模型结构\n",
    "\n",
    "加载训练得到的模型文件，用于推理。\n",
    "\n",
    "这里只加载模型，无回显。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 模型文件位置，比如由上面训练得到的位置train_url+'model',\n",
    "# 这个路径位置下面必须有.json和.params的模型文件\n",
    "ckpt = os.path.join(args.train_url, 'model/face')\n",
    "# 加载第几个epoch\n",
    "load_epoch = 1\n",
    "# 加载模型文件,sym为推理模型结构，arg_params, aux_params为模型参数\n",
    "sym, arg_params, aux_params = mx.model.load_checkpoint(ckpt, load_epoch)\n",
    "# 选择使用cpu进行推理，数据名默认为data，推理无需输入label\n",
    "mx_model = mx.mod.Module(symbol=sym, context=mx.cpu(),\n",
    "                                      data_names=['data'], label_names=None)\n",
    "# 绑定模型计算模块到底层计算引擎，绑定的数据大小默认为(1,3,112,112)\n",
    "mx_model.bind(for_training=False, data_shapes=[('data',(1,3,112,112))])\n",
    "# 设置模型的参数\n",
    "mx_model.set_params(arg_params, aux_params, allow_missing=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 推理计算并输出结果\n",
    "执行完这里会输出这张图片的分类结果和分类的可能性大小，模型输出整理成\\[分类结果，置信度\\]的形式。\n",
    "\n",
    "这里会输出人脸图片的类别预测结果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'predicted_label': 3, 'scores': [[3, 0.6772790551185608], [0, 0.18954646587371826]]}\n"
     ]
    }
   ],
   "source": [
    "# 模型结果后处理方法\n",
    "def _postprocess(data):\n",
    "    dim = len(data[0].shape)\n",
    "    if dim > 2:\n",
    "        data = mx.nd.array(np.squeeze(data.asnumpy(), axis=tuple(range(dim)[2:])))\n",
    "    # 将得到的结果排序，选择可能性最大的类别\n",
    "    sorted_prob = mx.nd.argsort(data[0], is_ascend=False)\n",
    "    top_prob = list(map(lambda x: int(x.asscalar()), sorted_prob[0:2]))\n",
    "    # 输出推理得到的类别和对应可能性\n",
    "    return {\"predicted_label\": class_names[top_prob[0]],\n",
    "            \"scores\": [[class_names[i], float(data[0, i].asscalar())] for i in top_prob]}\n",
    "\n",
    "# 进行前向推理\n",
    "mx_model.forward(mx.io.DataBatch(d))\n",
    "# 输出\n",
    "print(_postprocess(mx_model.get_outputs()[0]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 使用ModelArts SDK提交训练作业\n",
    "\n",
    "本节介绍如何使用ModelArts SDK提交训练作业，其中模型的训练脚本使用ModelArts自研的分布式深度学习框架实现。训练脚本见src目录下的face_test_in_moxing.py文件，大家可以查看并学习。\n",
    "\n",
    "分为以下几个步骤：\n",
    "\n",
    "1. 导入ModelArts SDK库\n",
    "2. 使用get_framework_list接口查询训练作业的引擎规格\n",
    "3. 使用get_train_instance_types接口查询训练作业的设备资源规格\n",
    "4. 提交训练作业\n",
    "5. 输出训练作业的日志"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 导入ModelArts SDK库\n",
    "\n",
    "导入ModelArts SDK，以及创建SDK客户端。 初始化Session实现与华为云资源的鉴权。\n",
    "\n",
    "此段代码无回显。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "import urllib3\n",
    "urllib3.disable_warnings()\n",
    "from modelarts.session import Session\n",
    "from modelarts.estimator import Estimator\n",
    "from modelarts.predictor import Predictor\n",
    "from modelarts.config.model_config import Params, ServiceConfig\n",
    "import json\n",
    "session = Session()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "####  使用get_framework_list接口查询训练作业的引擎规格\n",
    "\n",
    "此段代码打印训练作业支持的引擎规格列表。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[\n",
      " {\n",
      "  \"framework_type\": \"TensorFlow\",\n",
      "  \"framework_version\": \"TF-1.8.0-python2.7\"\n",
      " },\n",
      " {\n",
      "  \"framework_type\": \"TensorFlow\",\n",
      "  \"framework_version\": \"TF-1.8.0-python3.6\"\n",
      " },\n",
      " {\n",
      "  \"framework_type\": \"MXNet\",\n",
      "  \"framework_version\": \"MXNet-1.2.1-python2.7\"\n",
      " },\n",
      " {\n",
      "  \"framework_type\": \"MXNet\",\n",
      "  \"framework_version\": \"MXNet-1.2.1-python3.6\"\n",
      " },\n",
      " {\n",
      "  \"framework_type\": \"Caffe\",\n",
      "  \"framework_version\": \"Caffe-1.0.0-python2.7\"\n",
      " },\n",
      " {\n",
      "  \"framework_type\": \"Spark_MLlib\",\n",
      "  \"framework_version\": \"Spark-2.2.0-python2.7\"\n",
      " },\n",
      " {\n",
      "  \"framework_type\": \"Scikit_Learn\",\n",
      "  \"framework_version\": \"Scikit_Learn-0.18.1-python2.7\"\n",
      " },\n",
      " {\n",
      "  \"framework_type\": \"XGBoost\",\n",
      "  \"framework_version\": \"XGBoost-0.8-python2.7\"\n",
      " },\n",
      " {\n",
      "  \"framework_type\": \"PyTorch\",\n",
      "  \"framework_version\": \"PyTorch-1.0.0-python2.7\"\n",
      " },\n",
      " {\n",
      "  \"framework_type\": \"PyTorch\",\n",
      "  \"framework_version\": \"PyTorch-1.0.0-python3.6\"\n",
      " }\n",
      "]\n"
     ]
    }
   ],
   "source": [
    "print(json.dumps(Estimator.get_framework_list(session),indent = 1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "####  使用get_train_instance_types接口查询训练作业的设备资源规格\n",
    "\n",
    "此段代码打印训练作业支持的资源规格列表。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[\n",
      " \"modelarts.bm.gpu.8v100NV32\",\n",
      " \"modelarts.vm.cpu.2u\",\n",
      " \"modelarts.vm.gpu.p100\"\n",
      "]\n"
     ]
    }
   ],
   "source": [
    "print(json.dumps(Estimator.get_train_instance_types(session),indent = 1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 提交训练作业\n",
    "\n",
    "提交训练作业需要填写训练作业的相关参数。其中部分参数说明如下：\n",
    "\n",
    "hyperparameters为模型需要的超参,label表示参数名，value表示参数值，\n",
    "\n",
    "    调整模型的学习率lr、模型迭代次数epoch、模型的batch size大小等都会改变模型训练结果，\n",
    "    \n",
    "    有时候调整适当的超参，模型可以达到非常高的精度，默认参数不是最佳参数。\n",
    "    \n",
    "训练规格train_instance_type，可以从上段代码的输出的specs字段中挑选一个训练规格（spec_code）\n",
    "\n",
    "训练实例数量train_instance_count，默认为1\n",
    "\n",
    "训练模型输出路径output_path，默认和train_url一样\n",
    "\n",
    "启动成功后，会输出训练作业的启动日志，如JOBSTAT_INIT、JOBSTAT_RUNNING等。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Create directory */face_recognition/train_models/ successfully\n",
      "Create directory */face_recognition/train_models/ckpt/ successfully\n",
      "Create directory */face_recognition/train_models/log/ successfully\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:root:Start to copy 5 files.\n",
      "INFO:root:Finish copy.\n",
      "INFO:root:Start to copy 3 files.\n",
      "INFO:root:Finish copy.\n"
     ]
    }
   ],
   "source": [
    "# 训练任务根目录\n",
    "base_url = BUCKET_NAME + '/' + UNIQUE_ID + '/face_recognition/train_models/'\n",
    "session.create_directory(BUCKET_NAME, UNIQUE_ID + '/face_recognition/train_models/')\n",
    "\n",
    "\n",
    "# 训练代码目录\n",
    "app_url = base_url + 'src/'\n",
    "# 训练代码启动文件\n",
    "boot_file_url = app_url + 'face_test_in_moxing.py'\n",
    "# 训练输出目录\n",
    "train_url = base_url + 'ckpt/'\n",
    "session.create_directory(BUCKET_NAME,  UNIQUE_ID + '/face_recognition/train_models/ckpt/')\n",
    "\n",
    "# 数据集目录\n",
    "data_url = base_url + 'data/'\n",
    "#创建日志路径\n",
    "session.create_directory(BUCKET_NAME,  UNIQUE_ID + '/face_recognition/train_models/log/')\n",
    "# 日志目录\n",
    "log_url = base_url + 'log/'\n",
    "\n",
    "# 将源代码和数据上传到我们的OBS中，用于训练任务\n",
    "mox.file.copy_parallel('./src', 's3://'+base_url+'src')\n",
    "mox.file.copy_parallel(args.data_dir, 's3://'+base_url+args.data_dir)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "JOBSTAT_INIT\n",
      "JOBSTAT_WAITING\n",
      "JOBSTAT_RUNNING\n",
      "JOBSTAT_COMPLETED\n",
      "Job [ job-0429-212046 ] duration is 00:01:02\n"
     ]
    }
   ],
   "source": [
    "estimator = Estimator(modelarts_session=session,\n",
    "                      framework_type='MXNet',\n",
    "                      framework_version='MXNet-1.2.1-python3.6',\n",
    "                      code_dir='/'+app_url,\n",
    "                      boot_file='/'+boot_file_url,\n",
    "                      log_url='/'+log_url,\n",
    "                      hyperparameters=[{\"label\":\"data_dir\",\n",
    "                                        \"value\":'s3://'+data_url},\n",
    "                                       {\"label\":\"train_url\",\n",
    "                                        \"value\":'s3://'+train_url},\n",
    "                                       {\"label\":\"lr\",\"value\":\"0.001\"},\n",
    "                                       ],\n",
    "                      output_path='/'+train_url, # 注意：output_path在OBS中要预先创建好\n",
    "                      train_instance_type='modelarts.vm.gpu.p100',\n",
    "                      train_instance_count=1,\n",
    "                      job_description='test-face')\n",
    "# 启动训练任务\n",
    "job_id = estimator.fit(inputs='/'+data_url, wait=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上述代码执行完毕时，会不断输出训练任务的状态，直到状态为\"COMPLETED\"。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 为模型准备config.json文件\n",
    "mox.file.copy('./src/config.json', 's3://'+base_url+'ckpt/model/config.json')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "####  输出训练作业的日志\n",
    "\n",
    "此段可打印训练作业的日志"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "*/*/face_recognition/train_models/log/\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:obs:Successfully download file */*/face_recognition/train_models/log/job-job-0429-012020V0001-0-0-99tjc_default-stdout.log from OBS to local /tmp/log/log/job-job-0429-012020V0001-0-0-99tjc_default-stdout.log\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Successfully download file */*/face_recognition/train_models/log/job-job-0429-012020V0001-0-0-99tjc_default-stdout.log from OBS to local /tmp/log/log/job-job-0429-012020V0001-0-0-99tjc_default-stdout.log\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:obs:Successfully download file */*/face_recognition/train_models/log/job-job-0429-014504V0001-0-0-q7785_default-stdout.log from OBS to local /tmp/log/log/job-job-0429-014504V0001-0-0-q7785_default-stdout.log\n",
      "INFO:obs:Successfully download file */*/face_recognition/train_models/log/job-job-0429-100652V0001-0-0-w7728_default-stdout.log from OBS to local /tmp/log/log/job-job-0429-100652V0001-0-0-w7728_default-stdout.log\n",
      "INFO:obs:Successfully download file */*/face_recognition/train_models/log/job-job-0429-134751V0001-0-0-ltmwt_default-stdout.log from OBS to local /tmp/log/log/job-job-0429-134751V0001-0-0-ltmwt_default-stdout.log\n",
      "INFO:obs:Successfully download file */*/face_recognition/train_models/log/job-job-0429-144053V0001-0-0-tqfn4_default-stdout.log from OBS to local /tmp/log/log/job-job-0429-144053V0001-0-0-tqfn4_default-stdout.log\n",
      "INFO:obs:Successfully download file */*/face_recognition/train_models/log/job-job-0429-212046V0001-0-0-8qsgl_default-stdout.log from OBS to local /tmp/log/log/job-job-0429-212046V0001-0-0-8qsgl_default-stdout.log\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Successfully download file */*/face_recognition/train_models/log/job-job-0429-014504V0001-0-0-q7785_default-stdout.log from OBS to local /tmp/log/log/job-job-0429-014504V0001-0-0-q7785_default-stdout.log\n",
      "Successfully download file */*/face_recognition/train_models/log/job-job-0429-100652V0001-0-0-w7728_default-stdout.log from OBS to local /tmp/log/log/job-job-0429-100652V0001-0-0-w7728_default-stdout.log\n",
      "Successfully download file */*/face_recognition/train_models/log/job-job-0429-134751V0001-0-0-ltmwt_default-stdout.log from OBS to local /tmp/log/log/job-job-0429-134751V0001-0-0-ltmwt_default-stdout.log\n",
      "Successfully download file */*/face_recognition/train_models/log/job-job-0429-144053V0001-0-0-tqfn4_default-stdout.log from OBS to local /tmp/log/log/job-job-0429-144053V0001-0-0-tqfn4_default-stdout.log\n",
      "Successfully download file */*/face_recognition/train_models/log/job-job-0429-212046V0001-0-0-8qsgl_default-stdout.log from OBS to local /tmp/log/log/job-job-0429-212046V0001-0-0-8qsgl_default-stdout.log\n",
      "[decrypt_modelarts_aes_env_by_key_client] export S3_SECRET_ACCESS_KEY\n",
      "user: uid=1101(work) gid=1101(work) groups=1101(work)\n",
      "pwd: /home/work\n",
      "app_url: s3://*/*/face_recognition/train_models/src/\n",
      "boot_file: src/face_test_in_moxing.py\n",
      "log_url: /tmp/log/job-0429-012020.log\n",
      "command: src/face_test_in_moxing.py --data_url=s3://*/*/face_recognition/train_models/data/ --data_dir=s3://my-ml-course/my_student_id/face_recognition/train_models/data/ --train_url=s3://my-ml-course/my_student_id/face_recognition/train_models/ckpt/ --lr=0.001 --num_gpus=1 --train_url=s3://my-ml-course/my_student_id/face_recognition/train_models/ckpt/\n",
      "[modelarts_create_log] modelarts-pipe found\n",
      "2019-04-29 01:20:30,256 - modelarts-downloader.py[line:471] - INFO: Main: modelarts-downloader starting with Namespace(dst='./', recursive=True, src='s3://*/*/face_recognition/train_models/src/', trace=False, verbose=False)\n",
      "2019-04-29 01:20:30,524 - connectionpool.py[line:735] - INFO: Starting new HTTPS connection (1): obs.myhwclouds.com\n",
      "[modelarts_logger] modelarts-pipe found\n",
      "INFO:root:Using MoXing-v1.12.4-85d16fc8\n",
      "INFO:root:Using OBS-Python-SDK-3.1.2\n",
      "batch size:  8\n",
      "[01:20:43] src/io/iter_image_recordio_2.cc:170: ImageRecordIOParser2: s3://*/*/face_recognition/train_models/data/celeb_train.rec, use 3 threads for decoding..\n",
      "[01:20:50] src/io/iter_image_recordio_2.cc:170: ImageRecordIOParser2: s3://*/*/face_recognition/train_models/data/celeb_val.rec, use 3 threads for decoding..\n",
      "[01:21:05] src/operator/nn/./cudnn/./cudnn_algoreg-inl.h:107: Running performance tests to find the best convolution algorithm, this can take a while... (setting env variable MXNET_CUDNN_AUTOTUNE_DEFAULT to 0 to disable)\n",
      "INFO:root:Epoch[0] Train-loss=0.754292\n",
      "INFO:root:Epoch[0] Time cost=17.044\n",
      "INFO:root:Saved checkpoint to \"s3://*/*/face_recognition/train_models/ckpt/-0001.params\"\n",
      "INFO:root:Epoch[0] Validation-loss=2.632142\n",
      "exists  customize_service.py\n",
      "/home/work/anaconda3/lib/python3.6/site-packages/moxing/framework/file/src/obs/client.py:506: DeprecationWarning: key_file, cert_file and check_hostname are deprecated, use a custom context instead.\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "obs_log_path = log_url\n",
    "\n",
    "print(log_url)\n",
    "\n",
    "local_log_path = \"/tmp/log\"\n",
    "!rm -rf $local_log_path\n",
    "!mkdir -p $local_log_path\n",
    "session.download_data(obs_log_path, local_log_path)\n",
    "\n",
    "log_file = os.listdir(local_log_path+'/log/')[0]\n",
    "!cat $local_log_path/log/$log_file"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ModelArts的推理功能\n",
    "在使用ModelArts推理功能之前需要保证train_url（模型输出路径）下的文件结构如下所示\n",
    "\n",
    "         |--train_url\n",
    "           |--model\n",
    "              |--0000.params\n",
    "              |--symbol.json\n",
    "              |--config.json\n",
    "              |--customize_service.py\n",
    "\n",
    "    \n",
    "source_location，模型所在的位置\n",
    "\n",
    "execution_code，推理文件位置\n",
    "\n",
    "instance_type，推理规格，可以在上述计算资源中找到，推荐使用cpu资源\n",
    "\n",
    "instance_count，推理实例数量，默认为1\n",
    "\n",
    "本节分为以下几个步骤：\n",
    "1. 导入模型\n",
    "2. 部署模型\n",
    "3. 模型推理测试"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 导入模型\n",
    "\n",
    "导入训练作业生成的模型\n",
    "\n",
    "这里会输出模型对象。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "*/*/face_recognition/train_models/ckpt/\n",
      "Model name is model-0429-212804 \n",
      "The model source location is https://*.obs.myhwclouds.com/*/face_recognition/train_models/ckpt\n",
      "publishing\n",
      "published\n",
      "<modelarts.model.Model object at 0x7fd433e3e2e8>\n"
     ]
    }
   ],
   "source": [
    "# 调用create_model接口将训练结果导入模型管理\n",
    "model_path = BUCKET_NAME + \"/\" + UNIQUE_ID + \"/face_recognition/train_models/ckpt/\"\n",
    "print(model_path)\n",
    "model = estimator.create_model(model_algorithm = \"face\",\n",
    "                                  source_location = model_path,\n",
    "                                  execution_code = model_path + 'model/customize_service.py',\n",
    "                                  model_type=\"MXNet\")\n",
    "print (model)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 模型部署\n",
    "\n",
    "使用ModelArts SDK部署模型为一个在线服务。\n",
    "\n",
    "这段代码会输出模型部署日志。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Service name is celeb_face\n",
      "Deploy progress: 100%\n",
      "Deploy finished\n"
     ]
    }
   ],
   "source": [
    "# 调用deploy接口部署模型\n",
    "configs = []\n",
    "config1 = ServiceConfig(model_id=model.get_model_id(),\n",
    "                        weight=\"100\",\n",
    "                        instance_count=1,\n",
    "                        specification=\"modelarts.vm.cpu.2u\",\n",
    "                        envs={})\n",
    "configs.append(config1)\n",
    "service = estimator.deploy_predictor(service_name=\"celeb_face\",\n",
    "                                     description =\"face recognition service\",\n",
    "                                     configs=configs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 测试人脸识别服务\n",
    "\n",
    "我们选用本地data目录下的test.png，用ModelArts SDK调用服务的API："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'predicted_label': 4, 'scores': [[4, 0.9799331426620483], [1, 0.01852753385901451], [3, 0.0013976095942780375]]}\n"
     ]
    }
   ],
   "source": [
    "from modelarts.model import Predictor\n",
    "predictor_instance = Predictor(session, service.service_id)\n",
    "predict_result = predictor_instance.predict(\n",
    "    data = \"./data/test.png\",\n",
    "    data_type = \"images\")\n",
    "print(predict_result)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 删除推理资源 (可选)\n",
    "在使用完成后，及时删除推理使用的资源，可以节约费用。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Successfully delete the job 105495\n",
      "Delete the service 04c4f576-eaf7-4137-aab2-e7f832020b53 endpoint successfully.\n",
      "Delete the model ef5c200b-e6a0-4733-8198-da7e7900b106 endpoint successfully.\n"
     ]
    }
   ],
   "source": [
    "estimator.delete(job_id=job_id, model_id=model.model_id, service_id=service.service_id)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 小结\n",
    "\n",
    "在本次实验中，我们展示了如何使用深度学习技术构建一个人脸识别应用的全流程，并解释了模型中各个超参的含义，让大家具备了基本的调参能力。\n",
    "\n",
    "同时学习了ModelArts SDK的使用，以及如何通过SDK提交训练作业至ModelArts训练人脸识别模型，并将训练好的人脸识别模型使用SDK在ModelArts上部署成在线服务，并做推理测试。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "MXNet-1.2.1",
   "language": "python",
   "name": "mxnet-1.2.1"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
